/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Array;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.EventListener;
import javax.swing.event.EventListenerList;
/**
* Class that can hold the list of event listeners
* in a "weak" manner. The advantage is that the listener
* doesn't need to be explicitly removed. Because of the use
* of the <tt>WeakReference</tt> it gets forgotten
* automatically if it was garbage collected. There must
* be at least one non-weak reference to the listener
* (otherwise it would become garbage-collected).
* One of the ways is to store the listener in some instance
* variable in the class that adds the listener.
* Please note that the methods <tt>getListenerCount()</tt>
* and <tt>getListenerCount(Class t)</tt> give the count
* that doesn't reflect whether some listeners were garbage
* collected.
*
* @author Miloslav Metelka
* @version 1.00
*/
public class WeakEventListenerList extends EventListenerList {
private int listenerSize;
/**
*
*/
public synchronized Object[] getListenerList() {
int tgtInd = 0;
Object[] ret = new Object[listenerSize];
for (int i = 1; i < listenerSize; i += 2) {
Object l = ((WeakReference)listenerList[i]).get();
if (l != null) {
ret[tgtInd++] = listenerList[i - 1];
ret[tgtInd++] = l;
} else { // listener was garbage collected
/* Remove the listener and its class. This could be done
* in a more efficient but much less readable way batching
* the successive removes into one.
*/
System.arraycopy(listenerList, i + 1, listenerList, i - 1,
listenerSize - i - 1);
listenerSize -= 2;
i -= 2;
}
}
if (ret.length != tgtInd) {
Object[] tmp = new Object[tgtInd];
System.arraycopy(ret, 0, tmp, 0, tgtInd);
ret = tmp;
}
return ret;
}
public synchronized EventListener[] getListeners(Class t) {
int tgtInd = 0;
EventListener[] ret = (EventListener[])Array.newInstance(t, listenerSize);
for (int i = 0; i < listenerSize; i++) {
if (listenerList[i++] == t) {
EventListener l = (EventListener)((WeakReference)listenerList[i]).get();
if (l != null) {
ret[tgtInd++] = l;
} else { // listener was garbage collected
/* Remove the listener and its class. This could be done
* in a more efficient but much less readable way batching
* the successive removes into one.
*/
System.arraycopy(listenerList, i + 1, listenerList, i - 1,
listenerSize - i - 1);
listenerSize -= 2;
i -= 2;
}
}
}
if (ret.length != tgtInd) {
EventListener[] tmp = (EventListener[])Array.newInstance(t, tgtInd);
System.arraycopy(ret, 0, tmp, 0, tgtInd);
ret = tmp;
}
return ret;
}
/**
* Adds the listener as a listener of the specified type.
* @param t the type of the listener to be added
* @param l the listener to be added
*/
public synchronized void add(Class t, EventListener l) {
if (l==null) {
// In an ideal world, we would do an assertion here
// to help developers know they are probably doing
// something wrong
return;
}
if (!t.isInstance(l)) {
throw new IllegalArgumentException("Listener " + l +
" is not of type " + t);
}
if (listenerSize == 0) {
listenerList = new Object[] { t, new WeakReference(l) };
listenerSize = 2;
} else {
if (listenerSize == listenerList.length) { // reallocate
Object[] tmp = new Object[listenerSize * 2];
System.arraycopy(listenerList, 0, tmp, 0, listenerSize);
listenerList = tmp;
}
listenerList[listenerSize++] = t;
listenerList[listenerSize++] = new WeakReference(l);
}
}
/**
* Removes the listener as a listener of the specified type.
* @param t the type of the listener to be removed
* @param l the listener to be removed
*/
public synchronized void remove(Class t, EventListener l) {
if (l ==null) {
// In an ideal world, we would do an assertion here
// to help developers know they are probably doing
// something wrong
return;
}
if (!t.isInstance(l)) {
throw new IllegalArgumentException("Listener " + l +
" is not of type " + t);
}
// Is l on the list?
int index = -1;
for (int i = listenerSize - 2; i >= 0; i -= 2) {
if ((listenerList[i] == t)
&& (((WeakReference)listenerList[ i + 1]).get()).equals(l)
) {
index = i;
break;
}
}
// If so, remove it
if (index >= 0) {
System.arraycopy(listenerList, index + 2, listenerList, index,
listenerSize - index - 2);
listenerSize -= 2;
}
}
private synchronized void writeObject(ObjectOutputStream os) throws IOException {
os.defaultWriteObject();
// Save the non-null event listeners:
for (int i = 0; i < listenerSize; i += 2) {
Class t = (Class)listenerList[i];
EventListener l = (EventListener)((WeakReference)listenerList[i + 1]).get();
if ((l != null) && (l instanceof Serializable)) {
os.writeObject(t.getName());
os.writeObject(l);
}
}
os.writeObject(null);
}
private void readObject(ObjectInputStream is)
throws IOException, ClassNotFoundException {
is.defaultReadObject();
Object listenerTypeOrNull;
while (null != (listenerTypeOrNull = is.readObject())) {
EventListener l = (EventListener)is.readObject();
add(Class.forName((String)listenerTypeOrNull), l);
}
}
public synchronized String toString() {
StringBuffer sb = new StringBuffer("WeakEventListenerList: ");
sb.append(listenerSize / 2);
sb.append(" listeners:\n");
for (int i = 0; i < listenerSize; i += 2) {
sb.append(" type " + ((Class)listenerList[i]).getName());
sb.append(" listener " + ((WeakReference)listenerList[i + 1]).get());
}
return sb.toString();
}
}